package cn.ltgodm.sandbox.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.ltgodm.sandbox.enums.ExecuteCodeStatusEnum;
import cn.ltgodm.sandbox.exception.CompliedException;
import cn.ltgodm.sandbox.exception.ExecuteException;
import cn.ltgodm.sandbox.model.dto.ExecuteCodeRequest;
import cn.ltgodm.sandbox.model.dto.ExecuteCodeResponse;
import cn.ltgodm.sandbox.model.dto.ExecuteMessage;
import cn.ltgodm.sandbox.model.dto.JudgeInfo;
import cn.ltgodm.sandbox.service.CodeSandboxService;
import cn.ltgodm.sandbox.utils.ProcessUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * @author ltgodm
 * @date 2024-03-07 10:25:28
 */
@Slf4j
@Service
public class JavaNativeCodeSandboxImpl implements CodeSandboxService {

    @Value("${sandbox.globalCodePath:tmpCode}")
    private String globalCodePath;
    @Value("${sandbox.className:Main}")
    private String className;
    @Value("${sandbox.charset:UTF-8}")
    private String charset;

    @Override
    public ExecuteCodeResponse executeCode(ExecuteCodeRequest executeCodeRequest) {
        try {
            List<String> inputList = executeCodeRequest.getInputList();
            String code = executeCodeRequest.getCode();
            String language = executeCodeRequest.getLanguage();

            String userDir = System.getProperty("user.dir");
            String globalCodePathName = userDir + File.separator + globalCodePath;
            if (!FileUtil.exist(globalCodePathName)) {//判断目录是否存在
                FileUtil.mkdir(globalCodePath);
            }

            //把用户的代码隔离存放
            String userCodePath = globalCodePathName + File.separator + UUID.randomUUID();
            String userCodePathName = userCodePath + File.separator + className.concat(".java");
            File userCodeFile = FileUtil.writeString(code, userCodePathName, StandardCharsets.UTF_8);

            //编译
            compliedProcess(userCodeFile.getAbsolutePath());

            //运行
            ExecuteCodeResponse executeCodeResponse = runInteractProcess(inputList, userCodePath);

            //清除文件
            if (userCodeFile.getParentFile() != null) {
                boolean del = FileUtil.del(userCodePath);
                if (del) {
                    log.info("文件清理成功");
                } else {
                    log.error("文件清理失败");
                }
            }
            return executeCodeResponse;
        } catch (ExecuteException e) {
            return ProcessUtil.getErrorResponse(e);
        }
    }


    private void compliedProcess(String userCodePath) {
        //编译文件
        String compliedCMD = StrUtil.format("javac -encoding {} {}", charset, userCodePath);
        log.info("编译命令:{}", compliedCMD);
        try {
            Process compliedProcess = Runtime.getRuntime().exec(compliedCMD);
            ProcessUtil.getMessage(compliedProcess, "编译");
        } catch (IOException e) {
            throw new CompliedException("编译错误");
        }
    }


    /**
     * 运行代码，args参数
     *
     * @param inputList    出入用例
     * @param userCodePath 代码路径
     */
    private ExecuteCodeResponse runProcess(List<String> inputList, String userCodePath) {
        //构造返回结果
        List<ExecuteMessage> executeMessageList = new ArrayList<>();
        for (String inputCase : inputList) {
            String runCMD = StrUtil.format("java -Dfile.encoding={} -cp {} {} {}", charset, userCodePath, className, inputCase);
            log.info("运行命令:{},输入用例：{}", runCMD, inputCase);
            try {
                Process runProcess = Runtime.getRuntime().exec(runCMD);
                ExecuteMessage executeMessage = ProcessUtil.getMessage(runProcess, "运行");
                executeMessageList.add(executeMessage);
            } catch (IOException e) {
                throw new ExecuteException("执行异常");
            }
        }
        ExecuteCodeResponse executeCodeResponse = ExecuteCodeResponse.builder().status(ExecuteCodeStatusEnum.EXECUTE_SUCCESS.getValue()).build();
        executeCodeResponse.setStatus(ExecuteCodeStatusEnum.EXECUTE_SUCCESS.getValue());
        //处理返回结果
        List<String> outputList = new ArrayList<>();
        Long maxTime = 0L;
        for (ExecuteMessage executeMessage : executeMessageList) {
            //存在错误
            if (StrUtil.isNotEmpty(executeMessage.getErrorMessage())) {
                executeCodeResponse.setMessage(executeMessage.getErrorMessage());
                executeCodeResponse.setStatus(ExecuteCodeStatusEnum.USER_PROGRESS_ERROR.getValue());
                break;
            }
            //不存在错误
            maxTime = Math.max(maxTime, executeMessage.getTime());
            outputList.add(executeMessage.getMessage());
        }
        executeCodeResponse.setOutputList(outputList);
        JudgeInfo judgeInfo = new JudgeInfo();
//        judgeInfo.setMemory();
        judgeInfo.setTime(maxTime);
        executeCodeResponse.setJudgeInfo(judgeInfo);
        return executeCodeResponse;
    }

    /**
     * 运行代码，控制台输入
     *
     * @param inputList    出入用例
     * @param userCodePath 代码路径
     */
    private ExecuteCodeResponse runInteractProcess(List<String> inputList, String userCodePath) {
        List<ExecuteMessage> executeMessageList = new ArrayList<>();
        //执行文件
        for (String inputCase : inputList) {
            String runCMD = StrUtil.format("java -Dfile.encoding={} -cp {} {}", charset, userCodePath, className);
            log.info("运行命令:{},输入用例：{}", runCMD, inputCase);
            try {
                Process runProcess = Runtime.getRuntime().exec(runCMD);
                //从控制台中获取信息
                OutputStream outputStream = runProcess.getOutputStream();
                OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
                String[] cases = inputCase.split(" ");
                String join = StrUtil.join("\n\r", cases).concat("\n\r");
                outputStreamWriter.write(join);
                outputStreamWriter.flush();
                ExecuteMessage executeMessage = ProcessUtil.getMessage(runProcess, "运行");
                executeMessageList.add(executeMessage);
                //关闭资源
                outputStreamWriter.close();
                outputStream.close();
            } catch (IOException e) {
                throw new ExecuteException("执行异常");
            }

        }
        ExecuteCodeResponse executeCodeResponse = ExecuteCodeResponse.builder().status(ExecuteCodeStatusEnum.EXECUTE_SUCCESS.getValue()).build();
        //处理返回结果
        List<String> outputList = new ArrayList<>();
        Long maxTime = 0L;
        for (ExecuteMessage executeMessage : executeMessageList) {
            //存在错误
            if (StrUtil.isNotEmpty(executeMessage.getErrorMessage())) {
                executeCodeResponse.setMessage(executeMessage.getErrorMessage());
                executeCodeResponse.setStatus(ExecuteCodeStatusEnum.USER_PROGRESS_ERROR.getValue());
                break;
            }
            //不存在错误
            maxTime = Math.max(maxTime, executeMessage.getTime());
            outputList.add(executeMessage.getMessage());
        }
        executeCodeResponse.setOutputList(outputList);
        JudgeInfo judgeInfo = new JudgeInfo();
//        judgeInfo.setMemory();
        judgeInfo.setTime(maxTime);
        executeCodeResponse.setJudgeInfo(judgeInfo);
        return executeCodeResponse;
    }


}
